/* Copyright 2007 Sergejs Melderis (sergey.melderis@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/

package com.sm.performance;

import com.sm.model.PerformanceTestBeanWithConstructor;

import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.sql.ResultSet;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.DriverManager;

import com.sm.binder.ResultSetBinder;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import static com.sm.db.DbData.CONN_URL;

/**
 * @author Sergejs Melderis (sergey.melderis@gmail.com)
 */
public class PerformanceTestWithConstructor {

    static List<ResultSetTestBinder<PerformanceTestBeanWithConstructor>> binders =
            new ArrayList<ResultSetTestBinder<PerformanceTestBeanWithConstructor>>();

    static {
        binders.add(new ResultSetTestBinder<PerformanceTestBeanWithConstructor>("Manual Setters binder") {
            public List<PerformanceTestBeanWithConstructor> bind(ResultSet rs) throws Exception {
                List<PerformanceTestBeanWithConstructor> tests =
                        new ArrayList<PerformanceTestBeanWithConstructor>();
                while (rs.next()) {
                    PerformanceTestBeanWithConstructor test =
                            new PerformanceTestBeanWithConstructor();
                    test.setBigDecimalValue(rs.getBigDecimal(1));
                    test.setStringValue1(rs.getString(2));
                    test.setShortValue(rs.getShort(3));
                    test.setIntValue(rs.getInt(4));
                    test.setStringValue2(rs.getString(5));
                    test.setLongValue(rs.getLong(6));
                    test.setStringValue3(rs.getString(7));
                    test.setFloatValue(rs.getFloat(8));
                    test.setDoubleValue(rs.getDouble(9));
                    test.setStringValue4(rs.getString(10));
                    test.setDateValue(rs.getDate(11));
                    test.setStringValue5(rs.getString(12));
                    test.setTimeValue(rs.getTime(13));
                    test.setTimestampValue(rs.getTimestamp(14));
                    test.setStringValue6(rs.getString(15));
                    tests.add(test);
                }
                return tests;
            }
        });

        binders.add(new ResultSetTestBinder<PerformanceTestBeanWithConstructor>("Manual constructor binder ") {
            List<PerformanceTestBeanWithConstructor> bind(ResultSet rs) throws Exception {
                List<PerformanceTestBeanWithConstructor> tests =
                        new ArrayList<PerformanceTestBeanWithConstructor>();
                while (rs.next()) {
                    PerformanceTestBeanWithConstructor test =
                            new PerformanceTestBeanWithConstructor(
                                    rs.getBigDecimal(1),
                                    rs.getString(2),
                                    rs.getShort(3),
                                    rs.getInt(4),
                                    rs.getString(5),
                                    rs.getLong(6),
                                    rs.getString(7),
                                    rs.getFloat(8),
                                    rs.getDouble(9),
                                    rs.getString(10),
                                    rs.getDate(11),
                                    rs.getString(12),
                                    rs.getTime(13),
                                    rs.getTimestamp(14),
                                    rs.getString(15)
                            );
                    tests.add(test);
                }
                return tests;
            }
        });

        binders.add(new ResultSetTestBinder<PerformanceTestBeanWithConstructor>("ResultSetBinder") {
            public List<PerformanceTestBeanWithConstructor> bind(ResultSet rs) throws Exception {
                ResultSetBinder<PerformanceTestBeanWithConstructor> binder =
                        new ResultSetBinder<PerformanceTestBeanWithConstructor>(
                                PerformanceTestBeanWithConstructor.class);
                return binder.bindResultSet(rs);
            }
        });

        binders.add(new ResultSetTestBinder<PerformanceTestBeanWithConstructor>("DBUtil ResultSet Handler") {
            @SuppressWarnings("unchecked")
            public List<PerformanceTestBeanWithConstructor> bind(ResultSet rs) throws Exception {
                BeanListHandler handler = new BeanListHandler(PerformanceTestBeanWithConstructor.class);
                return (List<PerformanceTestBeanWithConstructor>) handler.handle(rs);
            }
        });
    }

    final int TIMES = 5;

    class ResultAverageInfo {
        Map<String, Double> totalTime = new HashMap<String, Double>();

        public void add(String name, double time) {
            if (!totalTime.containsKey(name))
                totalTime.put(name, 0.0);
            double current = totalTime.get(name);
            totalTime.put(name, current + time);
        }

        public void printTotalInfo() {
            for (Map.Entry<String, Double> entry : totalTime.entrySet()) {
                System.out.println("##### Average time for test " + entry.getKey() +
                        " is " + entry.getValue() / TIMES + " seconds ######");
            }
        }
    }


    @Test
    public void test() throws Exception {
        ResultAverageInfo info = new ResultAverageInfo();
        Connection conn = DriverManager.getConnection(CONN_URL);
        Statement st = null;
        ResultSet rs = null;
        for (int i = 0; i < TIMES; i++) {
            List<List<PerformanceTestBeanWithConstructor>> results =
                    new ArrayList<List<PerformanceTestBeanWithConstructor>>();
            for (ResultSetTestBinder<PerformanceTestBeanWithConstructor> binder : binders) {
                try {
                    st = conn.createStatement();
                    rs = st.executeQuery(
                            " SELECT BigDecimalValue, StringValue1, ShortValue, IntValue,"
                                    .concat(" StringValue2, LongValue, StringValue3, FloatValue, DoubleValue,")
                                    .concat(" StringValue4, DateValue, StringValue5,")
                                    .concat(" TimeValue, TimestampValue, StringValue6")
                                    .concat(" FROM PerformanceTestTable")
                    );

                    long start = System.nanoTime();
                    List<PerformanceTestBeanWithConstructor> beans = binder.bind(rs);
                    long end = System.nanoTime();
                    double time = (end - start) * Math.pow(10.0, -9.0);
                    info.add(binder.name, time);
                    System.out.println("Binding using " + binder.name);
                    System.out.println("Result list contains " + beans.size() + " objects");
                    System.out.println("Time to execute: " + time + " seconds");
                    results.add(beans);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                finally {
                    rs.close();
                    st.close();
                }
            }
            for (List<PerformanceTestBeanWithConstructor> beans1 : results) {
                for (List<PerformanceTestBeanWithConstructor> beans2 : results) {
                    assertEquals(beans1, beans2);
                    assertEquals(beans2, beans1);
                }
            }
            System.out.println();
        }
        conn.close();
        info.printTotalInfo();
    }

}
